﻿using Elasticsearch.Net;
using Microsoft.Extensions.Logging;
using Nest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Wicture.DbRESTFul;

namespace Wicture.EPC.OnlineQueryProxy.Core
{
    public class ESUtil
    {
        private static SniffingConnectionPool connectionPool;
        private static StaticConnectionPool _staticConnectionPool;
        private static ElasticClient _esClient;

        public static ElasticClient GetClient()
        {
            if (_esClient == null)
            {
                //链接elasticsearch 
                var connection = new MyCustomHttpConnection();
                var settings = new ConnectionSettings(GetConnectionPool(), connection);
                settings.RequestTimeout(TimeSpan.FromSeconds(AppConfig.EsRequestTimeout));
                settings.BasicAuthentication(AppConfig.ESUserName, AppConfig.ESPwd);
                if (AppConfig.EsKeepAliveEnable)
                {
                    settings.EnableTcpKeepAlive(TimeSpan.FromMilliseconds(AppConfig.EsKeepAliveTime), TimeSpan.FromMilliseconds(AppConfig.EsKeepAliveInterval));
                }
                _esClient = new ElasticClient(settings);
            }
            return _esClient;
        }

        private static IConnectionPool GetConnectionPool()
        {
            string connectionPoolConfig = AppConfig.EsConnectionPool;
            if (connectionPoolConfig == AppEnum.EsConnectionPool.Static.ToString())
            {
                return StaticConnectionPool;
            }
            else if (connectionPoolConfig == AppEnum.EsConnectionPool.Sniffing.ToString())
            {
                return SniffingConnectionPool;
            }
            return null;
        }

        //es连接池
        private static SniffingConnectionPool SniffingConnectionPool
        {
            get
            {
                if (connectionPool == null)
                {
                    var uris = new List<Uri>();
                    var urls = AppConfig.EsClientURL.Split(',');
                    foreach (var uri in urls)
                    {
                        uris.Add(new Uri(uri));
                    }
                    connectionPool = new SniffingConnectionPool(uris);
                }
                return connectionPool;
            }
        }

        //es连接池
        private static StaticConnectionPool StaticConnectionPool
        {
            get
            {
                if (_staticConnectionPool == null)
                {
                    var uris = new List<Uri>();
                    var urls = AppConfig.EsClientURL.Split(',');
                    foreach (var uri in urls)
                    {
                        uris.Add(new Uri(uri));
                    }
                    _staticConnectionPool = new StaticConnectionPool(uris);
                }
                return _staticConnectionPool;
            }
        }

        public static async Task<IDeleteIndexResponse> DeleteIndex(string indexName)
        {
            IDeleteIndexResponse response = null;
            try
            {
                if (!string.IsNullOrEmpty(indexName))
                {
                    IDeleteIndexRequest request = new DeleteIndexRequest(indexName);
                    response = await GetClient().DeleteIndexAsync(request);
                }
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "DeleteIndex 删除索引异常");
            }
            return response;
        }

        public static async Task<IBulkResponse> IndexBulk<T>(List<T> ts, string indexName, string typeName) where T : class
        {
            try
            {
                if (ts == null || ts.Count < 1) { return null; }
                var descriptor = new BulkDescriptor().Index(indexName).Type(typeName);
                foreach (var item in ts)
                {
                    descriptor.Index<T>(op => op.Document(item));
                }
                var res = await GetClient().BulkAsync(descriptor);
                if (res == null)
                {
                    LoggerManager.Logger.LogInformation("ES response is null");
                    return null;
                }
                return res;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "方法：IndexBulk 批处理异常");
            }
            return null;
        }

        /// <summary>
        /// 批处理，重载：数据路由
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="ts">实体列表</param>
        /// <param name="indexName">索引</param>
        /// <param name="typeName">类型</param>
        /// <returns></returns>
        public static async Task<IBulkResponse> IndexBulk<T>(List<T> ts, string indexName, string typeName, string[] routingName) where T : class
        {
            try
            {
                if (ts == null || ts.Count < 0) { return null; }
                var descriptor = new BulkDescriptor().Index(indexName).Type(typeName);
                foreach (var i in ts)
                {
                    string routing = string.Empty;
                    foreach (var array in routingName)
                    {
                        routing += i.GetType().GetProperty(array).GetValue(i, null).ToString();
                    }
                    descriptor.Index<T>(op => op.Document(i).Routing(routing));
                }
                var res = await GetClient().BulkAsync(descriptor);
                if (res == null)
                {
                    LoggerManager.Logger.LogInformation("ES response is null");
                }
                return res;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "方法：IndexBulk 批处理异常:");
            }
            return null;
        }

        /// <summary>
        /// 根据查询条件进行更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="indexName"></param>
        /// <param name="typeName"></param>
        /// <param name="script"></param>
        /// <returns></returns>
        public static async Task<IUpdateByQueryResponse> UpdateByQuery<T>(string indexName, string typeName, string script, Func<QueryContainerDescriptor<T>, QueryContainer> querySelector) where T : class
        {
            IUpdateByQueryResponse updateResponses = null;
            if (!string.IsNullOrEmpty(script))
            {
                try
                {
                    updateResponses = await GetClient()
                        .UpdateByQueryAsync<T>(u => u.Index(indexName).Type(typeName).Query(querySelector)
                         .Script(s => s.Source(script)
                           .Lang("painless"))
                             .Refresh()
                             .Conflicts(Elasticsearch.Net.Conflicts.Proceed)
                             .WaitForCompletion(false));
                }
                catch (Exception ex)
                {
                    LoggerManager.Logger.LogError(ex, "方法：UpdateByQuery 批处理异常");
                }
            }
            return updateResponses;
        }

        /// <summary>
        /// 根据查询条件进行删除
        /// </summary>
        /// <param name="indexName"></param>
        /// <param name="typeName"></param>
        /// <param name="qc"></param>
        /// <returns></returns>
        public static async Task<IDeleteByQueryResponse> DeleteByQuery(string indexName, string typeName, QueryContainer qc)
        {
            IDeleteByQueryResponse response = null;
            try
            {
                IDeleteByQueryRequest deleteQuery = new DeleteByQueryRequest(indexName, typeName) { Query = qc };
                response = await GetClient().DeleteByQueryAsync(deleteQuery);
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "方法：DeleteByQuery 批处理异常:");
            }
            return response;
        }
    }

    public class MyCustomHttpConnection : HttpConnection
    {
        //protected override void AlterServicePoint(ServicePoint requestServicePoint, RequestData requestData)
        //{
        //    base.AlterServicePoint(requestServicePoint, requestData);
        //    requestServicePoint.ConnectionLimit = ConfigManager.GetConfigObject("ConnectionLimit", 256);
        //    requestServicePoint.UseNagleAlgorithm = true;
        //}
    }
}
